<?php
	
	/**
	 * Verifies bootstrapper execution to ensure file
	 * necessities, and disabling direct access
	 */
	defined('ENGINE') or exit;
	
	/**
	 * MySQL driver
	 *
	 * This driver enables access to a MySQL 3+ based database using 
	 * the mysql database extension. If using MySQL 4.1+ the MySQLi 
	 * driver should be used as a better alternative for talking to 
	 * MySQL
	 */
	final class Database_Driver_MySQL extends Database
	{
		/**
		 * Driver name
		 */
		const DRIVER_NAME		= 'mysql';

		/**
		 * Link pointer, this contains the internal link 
		 * to the database from the driver
		 */
		protected $link;

		/**
		 * Returns if the current system supports the  driver, if this 
		 * method isn't called, a driver may start shutting down or 
		 * throwing random exceptions unexpectedly
		 */
		public function is_driver_supported()
		{
			return(extension_loaded('mysql'));
		}

		/**
		 * Connect to a database, if no connection isn't already 
		 * active
		 */
		public function connect(array $configuration = NULL)
		{
			if(!is_null($configuration))
			{
				$this->configuration = $configuration;
			}

			if(is_resource($this->link))
			{
				return(true);
			}

			ini_set('mysql.connect_timeout', (($timeout = $this->configuration['timeout']) !== false ? $timeout : 3));

			$port				= $this->configuration['port'];
			$connect_function 	= ($this->configuration['persistent'] ? 'mysql_pconnect' : 'mysql_connect');
			$hostname			= (($socket = $this->configuration['socket']) ? $this->configuration['hostname'] . ':' . $socket : $this->configuration['hostname']);

			if(empty($socket) && $port)
			{
				$hostname .= ':' . $port;
			}

			Engine::globals('error_reporting', false);

			if(($link = $connect_function($hostname, $this->configuration['username'], $this->configuration['password'], false, ($this->configuration['ssl'] ? MYSQL_CLIENT_SSL : 0))) === false || !mysql_select_db($this->configuration['database'], $link))
			{
				Engine::globals('error_reporting', true);

				$format = 'Database error: failed to connect database';

				if(DEBUG)
				{
					$format = 'Database error: [%d] %s';
				}

				throw new Engine_Basic_Exception($format, mysql_errno(), mysql_error());
			}

			Engine::globals('error_reporting', true);

			$this->link = $link;
		}

		/**
		 * Close a database connection
		 */
		public function close()
		{
			if(is_resource($this->link))
			{
				$retval 	= (boolean) mysql_close($this->link);
				$this->link 	= NULL;

				return($retval);
			}

			return(false);
		}

		/**
		 * Checks if a connection is active
		 */
		public function is_connected()
		{
			return(is_resource($this->link));
		}

		/**
		 * Checks if a variable is a connection of the same type 
		 * as the one used by the driver
		 */
		public function is_link($link)
		{
			return(is_resource($link) && get_resource_type($link) == 'mysql link');
		}

		/**
		 * Checks if the current connection is persistent
		 */
		public function is_persistent()
		{
			return($this->cfg('persistent'));
		}

		/**
		 * Checks if a variable is a result of the same type as 
		 * the one used by the driver
		 */
		public function is_result($result)
		{
			return(is_resource($result) && get_resource_type($result) == 'mysql result');
		}

		/**
		 * Get the error message from the last occured error
		 * error
		 */
		public function error()
		{
			if(!is_resource($this->link))
			{
				return(false);
			}

			return(mysql_error($this->link));
		}

		/**
		 * Get the error number from the last occured error
		 */
		public function errno()
		{
			if(!is_resource($this->link))
			{
				return(false);
			}

			return(mysql_errno($this->link));
		}
		
		/**
		 * List all of the tables in the database
		 */
		public function list_tables($use_buffer = true)
		{
			if(count($this->tables) && $use_buffer == true)
			{
				return($this->tables);
			}
					
			$query = $this->query("SHOW TABLES FROM `" . $this->configuration['database'] . "`");
			
			while($table = $query->fetch_array())
			{
				$tables[] = $table[0];
			}
			
			return($tables);
		}
		
		/**
		 * Determines if a database table exists
		 */
		public function table_exists($table = false, $use_buffer = true)
		{
			if($table != false)
			{
				$tables = $this->list_tables($use_buffer);
				
				foreach($tables as $table_name)
				{
					if($table == $table_name)
					{
						return(true);
					}
				}
			}
			
			return(false);
		}

		/**
		 * Get the last insert id from last executed SELECT statement
		 */
		public function insert_id()
		{
			if(!is_resource($this->link))
			{
				return(false);
			}

			return(mysql_insert_id($this->link));
		}

		/**
		 * Get the number of affected rows from last INSERT INTO/UPDATE/DELETE 
		 * operation.
		 */
		public function affected_rows($result)
		{
			if(!is_object($this->link))
			{
				return(false);
			}

			return((integer) mysql_affected_rows($this->link));
		}

		/**
		 * Escape a piece of data using the database specific 
		 * escape method
		 */
		public function escape($data)
		{
			if(!is_resource($this->link))
			{
				return(false);
			}

			return(mysql_real_escape_string((string) $data, $this->link));
		}

		/**
		 * Executes a query and returns the result on SELECT 
		 * statements
		 */
		public function query($sql)
		{
			$sql = (string) $sql;

			if(empty($sql) || !is_resource($this->link))
			{
				return(false);
			}
			elseif(func_num_args() > 1)
			{
				$args 		= func_get_args();
				$args[0]	= $sql;
				$sql 		= call_user_func_array('sprintf', $args);
			}
			
			$this->queries[] = $sql;

			Engine::globals('error_reporting', false);
			$query = mysql_query($sql);
			Engine::globals('error_reporting', true);

			if($query === true)
			{
				return(true);
			}
			elseif(is_resource($query))
			{
				return(new Database_Driver_MySQL_Result($this, $query));
			}
			elseif(!is_resource($query) && mysql_errno($this->link))
			{
				throw new Engine_SQL_Exception(mysql_error($this->link), mysql_errno($this->link));
			}

			return(false);
		}
		
		/**
		 * Parses SQL syntax to run multiple queries
		 */
		public function multi_query($sql)
		{
			$sql = explode(PHP_EOL, $sql);
			
			$query = '';
			
			foreach($sql as $line)
			{
				if(trim($line) != "" && strpos($line, "--") === false)
				{
					$query .= $line;
					
					if(preg_match("/(.*);/", $line))
					{
						$query = substr($query, 0, strlen($query)-1);
						
						$result = $this->query($query);
						$query = '';
					}
				}
			}
		}
	}

	/**
	 * MySQL result class for Engine
	 */
	final class Database_Driver_MySQL_Result extends Database_Result
	{
		/**
		 * The result resource
		 */
		protected $result;

		/**
		 * Frees the result from memory, and makes it unusable
		 */
		public function free()
		{
			if(is_resource($this->result))
			{
				mysql_free_result($this->result);
				$this->result = NULL;

				return(true);
			}

			return(false);
		}

		/**
		 * Checks whenever the result is freed or not
		 */
		public function is_freed()
		{
			return(is_null($this->result));
		}

		/**
		 * Get the number of rows in the result
		 */
		public function num_rows()
		{
			if(!is_resource($this->result))
			{
				return((isset($this->cached_num_rows) ? $this->cached_num_rows : 0));
			}

			return((integer) mysql_num_rows($this->result));
		}

		/**
		 * Fetch result with both associative and indexed indexes array
		 */
		public function fetch_array()
		{
			return($this->fetch(1));
		}

		/**
		 * Fetches the result and returns an associative array
		 */
		public function fetch_assoc()
		{
			return($this->fetch(2));
		}

		/**
		 * Fetches the result and returns an indexed array
		 */
		public function fetch_row()
		{
			return($this->fetch(3));
		}

		/**
		 * Fetches the result and returns an object, with overloaded 
		 * properties for rows names
		 */
		public function fetch_object()
		{
			return($this->fetch(4));
		}
		
		/**
		 * Quick reference for not repeating code when fetching a
		 * different type
		 */
		public function fetch($mode = NULL)
		{
			if(!is_resource($this->result) || !mysql_num_rows($this->result))
			{
				return(false);
			}

			switch($mode)
			{
				case(1):
				{
					return(mysql_fetch_array($this->result, MYSQL_NUM));
				}
				case(2):
				{
					return(mysql_fetch_array($this->result, MYSQL_ASSOC));
				}
				case(3):
				{
					return(mysql_fetch_row($this->result));
				}
				// case(4):
				default:
				{
					return(mysql_fetch_object($this->result, 'stdClass'));
				}
			}

			return(false);
		}
	}

?>